Skip to content

fix: avoid overwriting malformed global config#1339

Open
kiwigitops wants to merge 2 commits into
aws:mainfrom
kiwigitops:fix-invalid-global-config-overwrite
Open

fix: avoid overwriting malformed global config#1339
kiwigitops wants to merge 2 commits into
aws:mainfrom
kiwigitops:fix-invalid-global-config-overwrite

Conversation

@kiwigitops
Copy link
Copy Markdown

Summary

  • keep malformed global config files from being replaced during config updates
  • continue treating missing global config as an empty config
  • add a regression test for malformed config preservation

Fixes #1337.

Testing

  • npx prettier --check src/lib/schemas/io/global-config.ts src/cli/__tests__/global-config.test.ts
  • npx vitest run --project unit src/cli/__tests__/global-config.test.ts

@kiwigitops kiwigitops requested a review from a team May 21, 2026 02:35
@github-actions github-actions Bot added the size/s PR size: S label May 21, 2026
Copy link
Copy Markdown
Contributor

@Hweinstock Hweinstock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look! Looking at this change, we might need a broader refactor here to get the right behavior we're looking for.

Comment thread src/lib/schemas/io/global-config.ts Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like this catch will still silently swallow all errors from downstream. The result is that the user won't see a helpful error message of why the command failed when their config is invalid, but will see that the command failed.

I think this might take a broader refactor to bubble up this error correctly, (and maybe unify these read config methods)

@kiwigitops kiwigitops changed the title Avoid overwriting malformed global config fix: avoid overwriting malformed global config May 21, 2026
@kiwigitops
Copy link
Copy Markdown
Author

That makes sense. This patch handles the overwrite case, but it still leaves callers without a clean way to distinguish a missing config from a malformed config.

I can rework this around returning the parse/read error from the config-loading path if that matches the direction you had in mind.

Return a {success, error} result instead of a bare boolean so callers can distinguish a missing config (start fresh) from a malformed one (left unchanged, with the parse/read error surfaced). Telemetry enable/disable now report the actual failure reason rather than a generic write warning.
@github-actions github-actions Bot added size/s PR size: S and removed size/s PR size: S labels May 22, 2026
@kiwigitops
Copy link
Copy Markdown
Author

Reworked along those lines. updateGlobalConfig now returns { success, error? } instead of a bare boolean, and the config-loading path distinguishes a missing file (start fresh) from a malformed one: on a read/parse/schema failure it leaves the file untouched and returns the error, so callers can tell the two cases apart. telemetry enable/disable now surface the actual reason instead of a generic write warning. Updated the unit tests to cover the malformed case.

Copy link
Copy Markdown
Contributor

@Hweinstock Hweinstock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! The user experience makes sense to me. Few implementation notes, but I like the direction.

* (treated as an empty config) from a malformed one (read/parse/schema failure),
* so the caller can avoid clobbering a config it could not understand.
*/
async function loadConfigForUpdate(configFile: string): Promise<LoadConfigResult> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic feels a bit convoluted to me. Do you think it makes sense to directly check if the file exists (via fs.exists or something), then use that instead of the error?

} catch (error) {
return {
success: false,
error: `Config at ${configFile} is malformed and was left unchanged: ${toError(error).message}`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i don't think we need the "left unchanged"

}
}

type LoadConfigResult = { success: true; config: GlobalConfig } | { success: false; error: string };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we call the field errorMsg or make it store the full error object?

What you did is in-line with an existing codebase pattern I'm trying to undo where we maintain errors as strings everywhere. This makes sense for displaying to the user, but creates issues when we want programmatic logic based on errors (ex. check if downstream threw an error of type X). Without the object, we lose error type information and are forced to rely on brittle string matching to determine the cause of the error.


it('returns false on write failures', async () => {
const ok = await updateGlobalConfig(
it('does not overwrite malformed config and reports why', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice simple test!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: invalid agentcore config is silently overwritten.

2 participants